package com.tedu.aop.demo;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.sql.SQLOutput;
import java.util.Arrays;

/*
* Aspect注解:表示这个类是一个切面类
* */
@Aspect
@Component/*表示该类必须受Spring容器管理*/
public class LogAspect {
    /**
     * @Before注解用于标志当前方法的切入点，是在指定切入点(某方法)之前执行
     * 该注解需要传入一个参数，用于指定切入点信息(即目标方法)，要描述方法的签名
     *
     * 切入点描述的格式:
     * execution(方法签名)
     *
     * 方法签名的格式:
     * public int com.tedu.aop.demo.CalculatorImpl.add(..)
     * 修饰符 返回值 类的完全限定名.方法名(参数列表)
     *
     * 其中参数列表可以使用".."，表示任意参数
     *
     * 其他部分也可以使用通配符"*"
     * 例如，如果希望在CalculatorImpl任意方法前进行操作可以将方法名用“*”代替
     * execution(public int com.tedu.aop.demo.CalculatorImpl.*(..))
     *
     */
    //@Before("execution(public int com.tedu.aop.demo.CalculatorImpl.add(int,int))")
    //@Before("execution(public double com.tedu.aop.demo.CalculatorImpl.add(double ,double ))")

    //如果方法返回值为类型或者参数类型不对,不会报错,但是也发生任何作用
    //@Before("execution(public double com.tedu.aop.demo.CalculatorImpl.add(int ,int ))")
    //@Before("execution(public int com.tedu.aop.demo.CalculatorImpl.add(double ,double ))")

    //返回值类型使用*时,可以不明确返回值类型
    //@Before("execution(public * com.tedu.aop.demo.CalculatorImpl.add(double ,double ))")
    //@Before("execution(public * com.tedu.aop.demo.CalculatorImpl.add(..))")

    //如果参数是引用类型,要写清楚类的完全限定名(包名.类名)
    //@Before("execution(public * com.tedu.aop.demo.CalculatorImpl.add(java.util.List))")
    //@Before("execution(public * com.tedu.aop.demo.CalculatorImpl.add(String))")


    //@Before("execution(public int com.tedu.aop.demo.CalculatorImpl.add(..))")
    //@Before("execution(public int com.tedu.aop.demo.CalculatorImpl.*(..))")

    //无论方法返回值类型和参数类型是什么,只要是CalculatorImpl中的方法,前面都要切入
    /*@Before("execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))")
    public void before(JoinPoint joinPoint){
        //getTarget()获取当前切入的方法所属的对象,这里就是CalculatorImpl对象
        Object obj = joinPoint.getTarget();
        System.out.println("target:"+obj);

        ////通过该对象获取其对应类的类对象
        Class cls = obj.getClass();
        //通过类对象获取该类的完全限定名(包名.类名)
        System.out.println(cls.getName());
        //仅获取类名
        System.out.println(cls.getSimpleName());
        //通过切入点对象获取方法签名(获取切入对应方法的信息)
        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();
        System.out.println("方法:"+methodName);
        System.out.println("方法信息:"+signature);

        //获取参数
        Object[] args = joinPoint.getArgs();
        System.out.println("参数:"+ Arrays.toString(args));

        System.out.println("开始调用add方法...");

        Object object = joinPoint.getTarget();
        Class cls = object.getClass();
        String className = cls.getSimpleName();
        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();
        System.out.println("开始调用"+className+"."+methodName+"()方法");
    }*/
    /*@AfterReturning(
            value = "execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))",
            returning = "returnValue" //用于声明实际方法返回值传递给当前切面方法的哪个参数
    )
    public void afterReturning(JoinPoint joinPoint,Object returnValue){
        //getTarget()获取当前切入的方法所属的对象,这里就是CalculatorImpl对象
        //Object obj = joinPoint.getTarget();
        //通过该对象获取其对应类的类对象
        //Class cls = joinPoint.getTarget().getClass();
        //通过类对象获取该类的类名
        String className = joinPoint.getTarget().getClass().getSimpleName();

        //通过切入点对象获取方法签名(获取切入对应方法的信息)
        //获取方法名
        //Signature sig = joinPoint.getSignature();
        String methodName = joinPoint.getSignature().getName();
        System.out.println("调用"+className+"."+methodName+"()方法完毕,返回值为:"+returnValue);
    }*/
    /*
    * @AfterThrowing
    * 切入点是在方法执行过程中,如果方法出现异常,则执行当前切面方法
    *
    @AfterThrowing(
            value = "execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))",
            throwing = "e"//用于指定实际方法抛出的异常需要传递到当前方法的哪个参数上
    )
    public void afterThrowing(JoinPoint joinPoint,Exception e) {
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        System.out.println(className+"."+methodName+"()方法出现了异常:"+e.getMessage());
    }*/
    /*
    * @After
    * 切入点是在方法执行完毕之后
    * 无论方法是正常执行,还是中途出现异常,最终都会执行这个切面
    *
    @After("execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))")
    public void after(JoinPoint joinPoint){
        //获取类名
        String className = joinPoint.getTarget().getClass().getSimpleName();
        //获取方法名
        String methodName = joinPoint.getSignature().getName();
        System.out.println(className+"."+methodName+"()方法最终执行完毕");
    }*/
    /*
    * 环绕切入点(通知)
    * */
    @Around("execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))")
    public Object around(ProceedingJoinPoint joinPoint){
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        try {
            //@Before
            System.out.println(className+"."+methodName+"()方法开始调用了..");
            //使用切入点对象调用时机方法
            Object returnValue = joinPoint.proceed();//比如calculator.add(1,2);
            //@AfterReturning
            System.out.println(className+"."+methodName+"()方法调用完毕,返回值为:"+returnValue);
            return returnValue;
        } catch (Throwable e) {
            //@AfterThrowing
            System.out.println(className+"."+methodName+"()方法出现异常了,异常信息为:"+e.getMessage());
            throw new RuntimeException(e);
        }finally {
            //@After()
            System.out.println(className+"."+methodName+"()方法最终执行完毕");
        }
    }
}
